package com.hjs.test.util;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 运行前添加JVM参数 -Dlogging.config=classpath:\logback.xml
 * 以关闭redisson的debug日志
 */
public class RedissonRedisTokenUtils {
    static AtomicInteger token_seq = new AtomicInteger(0);
    private static final String LOCK_TEST_KEY = "redisson:lock:test";
    private static final String TOKEN_KEY = "redisson:token:01";
    private static volatile String redisToken;
    private static final long TOKEN_KEY_TTL = 20;
    public static String getToken(){
        redisToken = (String)RedissonClientHolder.getRedissonClient().getBucket(TOKEN_KEY).get();
        if(Objects.isNull(redisToken)){ // 如果Redis中的token为空，生成
            //RedissonClientHolder.getRedissonClient().getKeys().getKeys().forEach(System.out::println);
            RLock fairLock = RedissonClientHolder.getRedissonClient().getFairLock(LOCK_TEST_KEY);
            fairLock.lock(); // 分布式锁
            if(Objects.isNull(redisToken)){ // 再次判断DCL
                redisToken = generateToken();
                RedissonClientHolder.getRedissonClient().getBucket(TOKEN_KEY).set(redisToken, TOKEN_KEY_TTL, TimeUnit.SECONDS); // 存入Redis
            }
            fairLock.unlock();
        }
        return redisToken;
    }

    /**
     * 模拟生成token，编号递增
     * @return
     */
    private static String generateToken(){
        System.out.println("Thread-"+Thread.currentThread().getName()+" is generating the token...");
        String url = "http://newapi.zhihuishitang.net/gateway/openApi/accessToken/get";
        JSONObject param = new JSONObject();
        param.put("partnerId","bdckdj");
        param.put("partnerSecret","bdckdj123");
        try {
            String response = HttpRequest.post(url).setConnectionTimeout(15000).setReadTimeout(30000).body(param.toJSONString()).execute().body();
            //String response = httpRequest.method(Method.POST).execute().body();
            JSONObject object = JSON.parseObject(response);
            if (Objects.nonNull(object) && object.containsKey("data")) {
                JSONObject data = object.getJSONObject("data");
                if(Objects.nonNull(data)){
                    return data.getString("accessToken");
                }
            }
            return null;
        } catch (Exception ex) {
            return null;
        }
        //return String.format("the-token-%d", token_seq.getAndIncrement());
    }

    static class RedissonClientHolder{
        static volatile RedissonClient redissonClient;
        private static RedissonClient getRedissonClient(){
            if(Objects.isNull(redissonClient)){
                synchronized (Object.class){
                    if(Objects.isNull(redissonClient)) {
                        Config config = new Config();
                        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
                        redissonClient = Redisson.create(config);
                    }
                }

            }
            return redissonClient;
        }
    }

    private static void sleep(long sec){
        try {
            TimeUnit.SECONDS.sleep(sec);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    public static void main(String[] args) {
        int threadCount = 100;
        CountDownLatch latch = new CountDownLatch(threadCount);
        CountDownLatch latch2 = new CountDownLatch(threadCount);
        while (true){ // 每隔TOKEN_KEY_TTL秒，启动threadCount个线程，并发获取token并打印
            for(int i=0;i<threadCount;i++){
                new Thread(() ->{
                    latch.countDown();
                    try {
                        latch.await();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println(getToken());
                    latch2.countDown();
                },String.valueOf(i)).start();
            }
            try {
                latch2.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            sleep(20);
        }

    }
}
